typedef struct
{
  volatile unsigned int ACTLR;
  volatile unsigned int KEYR;
  volatile unsigned int OBKEYR;
  volatile unsigned int STATR;
  volatile unsigned int CTLR;
  volatile unsigned int ADDR;
  volatile unsigned int RESERVED;
  volatile unsigned int OBR;
  volatile unsigned int WPR;
  volatile unsigned int MODEKEYR;
} FLASH_TypeDef_ch32f103;
#define FLASH               ((FLASH_TypeDef_ch32f103 *) (0x40000000 + 0x20000 + 0x2000))
#define ch32RDP_Key                  ((unsigned short)0x00A5)
#define ch32FLASH_KEY1               ((unsigned int)0x45670123)
#define ch32FLASH_KEY2               ((unsigned int)0xCDEF89AB)
/* FLASH_Flags */	
#define FLASH_FLAG_BSY                 ((unsigned int)0x00000001)  /* FLASH Busy flag */
#define FLASH_FLAG_EOP                 ((unsigned int)0x00000020)  /* FLASH End of Operation flag */
#define FLASH_FLAG_PGERR               ((unsigned int)0x00000004)  /* FLASH Program error flag */
#define FLASH_FLAG_WRPRTERR            ((unsigned int)0x00000010)  /* FLASH Write protected error flag */
#define FLASH_FLAG_OPTERR              ((unsigned int)0x00000001)  /* FLASH Option Byte error flag */

#define ProgramTimeout                  ((unsigned int )0x00002000)
#define FLASH_PAGES_TO_BE_PROTECTED    ((unsigned int)0x00008000)

#define FLASH_FLAG_BANK1_BSY                 FLASH_FLAG_BSY       /* FLASH BANK1 Busy flag*/
#define FLASH_FLAG_BANK1_EOP                 FLASH_FLAG_EOP       /* FLASH BANK1 End of Operation flag */
#define FLASH_FLAG_BANK1_PGERR               FLASH_FLAG_PGERR     /* FLASH BANK1 Program error flag */
#define FLASH_FLAG_BANK1_WRPRTERR            FLASH_FLAG_WRPRTERR  /* FLASH BANK1 Write protected error flag */


/* Flash Control Register bits */
#define CR_PG_Set                ((unsigned int)0x00000001)
#define CR_PG_Reset              ((unsigned int)0xFFFFFFFE)
#define CR_PER_Set               ((unsigned int)0x00000002)
#define CR_PER_Reset             ((unsigned int)0xFFFFFFFD)
#define CR_MER_Set               ((unsigned int)0x00000004)
#define CR_MER_Reset             ((unsigned int)0xFFFFFFFB)
#define CR_OPTPG_Set             ((unsigned int)0x00000010)
#define CR_OPTPG_Reset           ((unsigned int)0xFFFFFFEF)
#define CR_OPTER_Set             ((unsigned int)0x00000020)
#define CR_OPTER_Reset           ((unsigned int)0xFFFFFFDF)
#define CR_STRT_Set              ((unsigned int)0x00000040)
#define CR_LOCK_Set              ((unsigned int)0x00000080)
#define CR_PAGE_PG 	             ((unsigned int)0x00010000)
static inline __attribute__((always_inline))
unsigned int FLASH_GetWriteProtectionOptionByte(void)
{
  return (unsigned int)(FLASH->WPR);
}
static inline __attribute__((always_inline))
void FLASH_ClearFlag( unsigned int FLASH_FLAG )
{
    FLASH->STATR = FLASH_FLAG;
}
static inline __attribute__((always_inline))
unsigned int FLASH_GetBank1Status( void )
{
    unsigned int flashstatus = 0;

    if( ( FLASH->STATR & FLASH_FLAG_BANK1_BSY ) == FLASH_FLAG_BSY )
    {
        flashstatus = 1u;//busy
    }
    else
    {
        if( ( FLASH->STATR & FLASH_FLAG_BANK1_PGERR ) != 0 )
        {
            flashstatus = 2u;
        }
        else
        {
            if( ( FLASH->STATR & FLASH_FLAG_BANK1_WRPRTERR ) != 0 )
            {
                flashstatus = 3u;
            }
            else
            {
                flashstatus = 0;
            }
        }
    }
    return flashstatus;
}
// 0 ok
// 1 timeout
// 2 PGERR
// 3 WRPR
static inline __attribute__((always_inline))
unsigned int FLASH_WaitForLastOperation( unsigned int Timeout )
{
    unsigned int status = 0,ret = 0;

    do
    {
        status = FLASH_GetBank1Status();
        Timeout--;
    }while( ( status == 1u ) && ( Timeout != 0x00 ) );
    if( Timeout == 0x00 )
    {
        ret = 1;
    }
    else
    {
        ret = status;
    }
    return ret;
}
static inline __attribute__((always_inline))
unsigned int FLASH_ProgramHalfWord( unsigned int Address, unsigned short Data )
{
    unsigned int status = 0;
    status = FLASH_WaitForLastOperation( ProgramTimeout );
    if(status == 0)
    {
        FLASH->CTLR |= CR_PG_Set;
        *( volatile unsigned short * )Address = Data;
        status = FLASH_WaitForLastOperation( ProgramTimeout );
        FLASH->CTLR &= CR_PG_Reset;
    }

    return status;
}
// 0 ok
// 1 timeout
// 2 PGERR
// 3 WRPR
void ch32_FlashWrite(unsigned int Addr,char *buf,unsigned int len)
{
    unsigned int i,WRPR_Value,EndAddr = Addr + len; 
    unsigned short Data;
    unsigned int stat,ret=1;
    WRPR_Value = FLASH_GetWriteProtectionOptionByte();
    if( ( WRPR_Value & FLASH_PAGES_TO_BE_PROTECTED ) != 0x00 )
    {
        FLASH->STATR = FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR;
        while( ( Addr < EndAddr ) && ( stat == 0 ) )
        {
            Data = buf[0] | (buf[1] << 8);
            buf += 2;
            stat = FLASH_ProgramHalfWord( Addr, Data );
            Addr = Addr + 2;
        }
        if(stat !=0)
        {
            if(stat == 1)
            {
                __asm("movs r0,#1");
                __asm("bkpt 0");
            }
            else if(stat == 2)
            {
                __asm("movs r0,#2");
                __asm("bkpt 0");
            }
            else if(stat == 3)
            {
                __asm("movs r0,#3");
                __asm("bkpt 0");
            }
            else if(stat == 4)
            {
                __asm("movs r0,#4");
                __asm("bkpt 0");
            }
        }
    }
    else
    {
        __asm("movs r0,#11");
        __asm("bkpt 0");
    }
    __asm("movs r0,#0");
    __asm("bkpt 0");
}